#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <pthread.h>
#include <string>
#include <sstream>
#include <iostream>

#include "crc-ccitt.h"

#include "syscore.h"
#include "apis.h"
#include "pactrl.h"
#include "pa_hx.h"
using namespace std;

const char *uart_serial_dev[] = {"/dev/ttyS0", "/dev/ttyS1", "/dev/ttyS2", "/dev/ttyS3", "/dev/ttyS4", "/dev/ttyS5"};

int32_t  pa_serial_fd = 0;

int32_t  pa_number = 0;
int32_t tempwarnThreshold = 0;
mod_addr_t hx_pa_addr[PA_NUM] = {
{HX_MOD_ADDR_FUNC_PA, 0X31},
{HX_MOD_ADDR_FUNC_PA, 0X32},
{HX_MOD_ADDR_FUNC_PA, 0X33},
{HX_MOD_ADDR_FUNC_PA, 0X34},
{HX_MOD_ADDR_FUNC_PA, 0X35},
{HX_MOD_ADDR_FUNC_PA, 0X36},
{HX_MOD_ADDR_FUNC_PA, 0X37},
{HX_MOD_ADDR_FUNC_PA, 0X38},
{HX_MOD_ADDR_FUNC_PA, 0X39},
{HX_MOD_ADDR_FUNC_PA, 0X3a},
{HX_MOD_ADDR_FUNC_PA, 0X3b}
};

bool hx_pa_present[PA_NUM] = {false, false, false, false, false, false, false, false, false, false, false};

int32_t open_uart_serial_port(int32_t port_num)
{
    int32_t ret = 0;

    int32_t fd = open(uart_serial_dev[port_num], O_RDWR | O_NOCTTY | O_NDELAY);
    if( -1 == fd ) 
    {
        dbgprintf(DEBUG_ERROR, stderr, "modId[0x%x] Can't open serial sort %s \n", MODULE_PA, uart_serial_dev[port_num], MODULE_PA);
        return (-1);
    }

    dbgprintf(DEBUG_NOTICE, stdout, "modId[0x%x] Open serial sort %s successfully \n", MODULE_PA, uart_serial_dev[port_num]);

    /* set blocking state */
    ret = fcntl(fd, F_SETFL , 0);
    if( ret < 0 )
    {
        dbgprintf(DEBUG_ERROR, stderr, "modId[0x%x] fcntl operation on fd(%d) failed\n", MODULE_PA, fd);
    }

    /* test tty */
    if( isatty(STDIN_FILENO) == 0 )
    {
        dbgprintf(DEBUG_ERROR, stderr, "modId[0x%x] standard input is not a terminal device\n", MODULE_PA);
    }
    else
    {
        dbgprintf(DEBUG_NOTICE, stdout, "modId[0x%x] standard input is a tty\n", MODULE_PA);
    }

    dbgprintf(DEBUG_NOTICE, stdout, "modId[0x%x] Open %s done, get fd = %d.\n", MODULE_PA, uart_serial_dev[port_num], fd);
    return fd;
}

int32_t set_uart_serial_opt(int32_t fd, int32_t nSpeed, int32_t nBits, char nEvent, int32_t nStop)
{
    struct termios newtio, oldtio;
    if( tcgetattr(fd, &oldtio) != 0 )
    {                       
        dbgprintf(DEBUG_ERROR, stderr, "modId[0x%x] Get fd(%d) attribute failed \n", MODULE_PA, fd);                             
        return -1;
    }

    bzero(&newtio, sizeof (newtio));
    /* Active CLOCAL and CREAD */
    newtio.c_cflag |= CLOCAL | CREAD;

    switch( nSpeed )
    {
    case 2400:
        cfsetispeed(&newtio, B2400);
        cfsetospeed(&newtio, B2400);
        break;
    case 4800:
        cfsetispeed(&newtio, B4800);
        cfsetospeed(&newtio, B4800);
        break;
    case 9600:
        cfsetispeed(&newtio, B9600);
        cfsetospeed(&newtio, B9600);
        break;
    case 19200:
        cfsetispeed(&newtio, B19200);
        cfsetospeed(&newtio, B19200);
        break;
    case 115200:
        cfsetispeed(&newtio, B115200);
        cfsetospeed(&newtio, B115200);
        break;
    case 460800:
        cfsetispeed(&newtio, B460800);
        cfsetospeed(&newtio, B460800);
        break;
    default:
        cfsetispeed(&newtio, B9600);
        cfsetospeed(&newtio, B9600);
        break;
    }

    /* Set data bit */
    newtio.c_cflag &= ~CSIZE;
    switch( nBits ) 
    {
    case 7:
        newtio.c_cflag |= CS7;
        break;
    case 8:
        newtio.c_cflag |= CS8;
        break;
    }

    switch( nEvent )
    {
    case 'O':
        newtio.c_cflag |= PARENB;
        newtio.c_cflag |= PARODD;
        newtio.c_iflag |= (INPCK | ISTRIP);
        break;
    case 'E':
        newtio.c_iflag |= (INPCK | ISTRIP);
        newtio.c_cflag |= PARENB;
        newtio.c_cflag &= ~PARODD;
        break;
    case 'N':
        newtio.c_cflag &= ~PARENB;
        break;
    }
  
    if( nStop == 1 )
    {
        newtio.c_cflag &= ~CSTOPB;
    }
    else if (nStop == 2)
    {
        newtio.c_cflag |= CSTOPB;
    }

    newtio.c_cc[VTIME] = 0;
    newtio.c_cc[VMIN] = 0;

    /* flush buffered data which are not sent yet */
    tcflush(fd, TCIFLUSH);

    /* Apply the new setting now */
    if( (tcsetattr(fd, TCSANOW, &newtio)) != 0 )
    { 
        dbgprintf(DEBUG_ERROR, stderr, "modId[0x%x] Set fd(%d) attribute failed \n", MODULE_PA, fd);
        return -1;
    }

    dbgprintf(DEBUG_NOTICE, stdout, "modId[0x%x] Set serial port option done\n", MODULE_PA);
    return 0;
}

bool init_uart_serial(int32_t port_num)
{
    if( pa_serial_fd != 0 )
    {
        dbgprintf(DEBUG_ERROR, stderr, "modId[0x%x] Serial port initialized \n", MODULE_PA);
        return false;
    }

    pa_serial_fd = open_uart_serial_port(port_num);
    if( pa_serial_fd < 0 )
    {
        dbgprintf(DEBUG_ERROR, stderr, "modId[0x%x] Open serial port failed \n", MODULE_PA);
        return false;
    }

    return true;
}

void term_uart_serial()
{
    sleep(3);
    close(pa_serial_fd);
    pa_serial_fd = 0;
}

pa_status pa_status_polling()
{
	pa_status status;
    memset(&status, 0x0, sizeof(pa_status));

    hx_pa_status hxPaStatus;
    memset(&hxPaStatus, 0, sizeof(hx_pa_status));   

    std::stringstream allMsg;
    std::string paMsg;
    for (int i = 0; i < PA_NUM; i++)
    {
        if(hx_pa_present[i])
        {
            memset(&hxPaStatus, 0, sizeof(hx_pa_status));

		    if( pa_ret_ok !=  hx_get_pa_temp(pa_serial_fd, &hx_pa_addr[i], &hxPaStatus) )
            {
                paMsg.assign("Temp/");
            }

		    if( pa_ret_ok !=  hx_get_up_att(pa_serial_fd, &hx_pa_addr[i], &hxPaStatus) )
            {
                paMsg.append("UP_ATT/");
            }

		    if( pa_ret_ok !=  hx_get_dn_att(pa_serial_fd, &hx_pa_addr[i], &hxPaStatus) )
            {
                paMsg.append("DN_ATT/");
            }

	  	    if( pa_ret_ok !=  hx_get_dn_power(pa_serial_fd, &hx_pa_addr[i], &hxPaStatus) )
            {
                paMsg.append("DN_Power/");
            }

	  	    if( pa_ret_ok !=  hx_get_dn_10_power(pa_serial_fd, &hx_pa_addr[i], &hxPaStatus) )
            {
                paMsg.append("DN_10_Power/");
            }

	  	    if( pa_ret_ok !=  hx_get_pa_state(pa_serial_fd, &hx_pa_addr[i], &hxPaStatus) )
            {
                paMsg.append("State/");
            }

	  	    if( pa_ret_ok !=  hx_get_pa_10_swr(pa_serial_fd, &hx_pa_addr[i], &hxPaStatus) )
            {
                paMsg.append("Sw10radio/");
            }

		    if( pa_ret_ok != hx_get_pa_switch(pa_serial_fd, &hx_pa_addr[i], &hxPaStatus) )
            {
                paMsg.append("Switch/");
            }


            if(paMsg.length() > 0) 
            {
                allMsg << "Get PA[" << i << "]" << " Status: " << paMsg << "failed." << endl;
                PALogError(allMsg.str().c_str());
                paMsg.clear();
                allMsg.clear();
            }
	  
            status.pa_present[i] = 1;
            status.pa_state[i] = hxPaStatus.state;
            status.pa_addr[i] = hx_pa_addr[i].addr_code;   
            status.pa_status[i].pa_temp = hxPaStatus.temp;
            status.pa_status[i].pa_upatt = hxPaStatus.upatt;
            status.pa_status[i].pa_dnatt= hxPaStatus.dnatt;
            status.pa_status[i].pa_dn_power = hxPaStatus.dnpower;
	        status.pa_status[i].pa_dn_10_power= hxPaStatus.dn10power;
	        status.pa_status[i].state = hxPaStatus.state;
            status.pa_status[i].pa_sw10ratio= hxPaStatus.sw10ratio;	
            status.pa_status[i].pa_switch= hxPaStatus.on_off;
        }
    }

    return status;
}

pa_ret_t set_pa_switch(uint8_t addr_code, uint8_t on_off)
{
	pa_ret_t ret = pa_ret_ok;

    for (int i = 0; i < PA_NUM; i++)
    {
        if(addr_code == (hx_pa_addr[i].addr_code & 0x0F))
        {
            if( hx_pa_present[i] )
            {
                ret = hx_set_pa_switch(pa_serial_fd, &hx_pa_addr[i], on_off);
                if( pa_ret_ok != ret )
                {
                    PALogError("set mw pa switch failed, addr=%d", hx_pa_addr[i].addr_code);
                    return ret;
                }
            }
            else
            {
                ret = pa_not_found;
            }
        }
    }

    return ret;
}

pa_ret_t set_pa_reset(uint8_t addr_code)
{
  	pa_ret_t ret = pa_ret_ok;

    for (int i = 0; i < PA_NUM; i++)
    {
        if(addr_code == (hx_pa_addr[i].addr_code & 0x0F))
        {
            if( hx_pa_present[i] )
            {
        	    ret = hx_set_mod_reset(pa_serial_fd, &hx_pa_addr[i]);
                if( pa_ret_ok != ret )
                {
                    PALogError("set hx pa reset failed, addr=%d", hx_pa_addr[i].addr_code);
                    return ret;
                }
            }
            else
            {
                ret = pa_not_found;
            }
        }
    }

    return ret;
}

pa_ret_t set_up_att(uint8_t addr_code, uint8_t up_att)
{
    pa_ret_t ret = pa_ret_ok;
 
    for (int i = 0; i < PA_NUM; i++)
    {
        if(addr_code == (hx_pa_addr[i].addr_code & 0x0F))
        {
            if( hx_pa_present[i] )
            {
                ret = hx_set_up_att(pa_serial_fd, &hx_pa_addr[i], up_att);
                
                if( pa_ret_ok != ret  )
                {
                    PALogError("set hx up att failed, addr=%d", hx_pa_addr[i].addr_code);
                    return ret;
                }
            }
            else
            {
                ret = pa_not_found;
            }
        }
    }

    return ret;
}

pa_ret_t set_dn_att(uint8_t addr_code, uint8_t dn_att)
{
	pa_ret_t ret = pa_ret_ok;  

    for (int i = 0; i < PA_NUM; i++)
    {
        if(addr_code == (hx_pa_addr[i].addr_code & 0x0F))
        {
            if( hx_pa_present[i] )
            {
        	    ret = hx_set_dn_att(pa_serial_fd, &hx_pa_addr[i], dn_att);
                if( pa_ret_ok != ret  )
                {
                    PALogError("set hx dn att failed, addr=%d", hx_pa_addr[i].addr_code);
                    return ret;
                }
            }
            else
            {
                ret = pa_not_found;
            }
        }
    }

    return ret;
}

pa_ret_t set_power_threshold(uint8_t addr_code, uint8_t power)
{
	pa_ret_t ret = pa_ret_ok;  

    for (int i = 0; i < PA_NUM; i++)
    {
        if(addr_code == (hx_pa_addr[i].addr_code & 0x0F))
        {
            if( hx_pa_present[i] )
            {
        	    ret = hx_set_pa_power_threshold(pa_serial_fd, &hx_pa_addr[i], power);
                if( pa_ret_ok != ret  )
                {
                    PALogError("set hx power threshold failed, addr=%d", hx_pa_addr[i].addr_code);
                    return ret;
                }
            }
            else
            {
                ret = pa_not_found;
            }
        }
    }

    return ret;
}

pa_ret_t set_temp_threshold(uint8_t addr_code, uint8_t temp)
{
    pa_ret_t ret = pa_ret_ok;
 
    for (int i = 0; i < PA_NUM; i++)
    {
        if(addr_code == (hx_pa_addr[i].addr_code & 0x0F))
        {
            if( hx_pa_present[i] )
            {
                ret = hx_set_pa_temp_threshold(pa_serial_fd, &hx_pa_addr[i], temp);
                
                if( pa_ret_ok != ret  )
                {
                    PALogError("set hx temp threshold failed, addr=%d", hx_pa_addr[i].addr_code);
                    return ret;
                }
            }
            else
            {
                ret = pa_not_found;
            }
        }
    }

    return ret;
}


uint32_t get_pa_number()
{
    return pa_number;
}

pa_ret_t get_pa_status(uint8_t addr_code, uint32_t entry, uint8_t *value)
{
    pa_ret_t ret = pa_ret_ok;
    lpa_status_t lpa_status = { 0 };	

	switch( entry ) 
    {
	    case PA_STATUS_SWITCH:
             for (int i = 0; i < PA_NUM; i++)
             {
                 if(addr_code == (hx_pa_addr[i].addr_code & 0x0F))
                 {
                     if( hx_pa_present[i] )
                     {
                         hx_pa_status status;
 
	                     ret = hx_get_pa_switch(pa_serial_fd, &hx_pa_addr[i], &status); 
                         if( pa_ret_ok != ret)
                         {
                             PALogError("get hx pa switch failed.");

                             return ret;
                         }

                         lpa_status.pa_switch= status.on_off;
                     }
                     else
                     {
                         ret = pa_not_found;
                     }
                 }
             }

             *value = lpa_status.pa_switch;   
             break;
			
        case PA_STATUS_STATE:
            for (int i = 0; i < PA_NUM; i++)
            {
                if(addr_code == (hx_pa_addr[i].addr_code & 0x0F))
                {
                    if( hx_pa_present[i] )
                    {
                        hx_pa_status status;
                        ret = hx_get_pa_state(pa_serial_fd, &hx_pa_addr[i], &status);
                        if( pa_ret_ok != ret)
                        {
                	        PALogError("get hx pa state failed.");
                	        return ret;
            	        }
                        lpa_status.state = status.state;
                    }
                    else
                    {
                        ret = pa_not_found;
                    }
                }
            }        

            *value = lpa_status.state & 0x1;
            break;

        case PA_STATUS_TEMP:
            for (int i = 0; i < PA_NUM; i++)
            {
                if(addr_code == (hx_pa_addr[i].addr_code & 0x0F))
                {
                    if( hx_pa_present[i] )
                    {
                        hx_pa_status status;
                        ret = hx_get_pa_temp(pa_serial_fd, &hx_pa_addr[i], &status); 
                        if( pa_ret_ok != ret)
                        {
                	        PALogError("get hx pa temp failed.");
                	        return ret;
                        }
                        lpa_status.pa_temp= status.temp;
                    }
                    else
                    {
                        ret = pa_not_found;
                    }
                }
            }  

            *value = lpa_status.pa_temp;
            break;

        case PA_STATUS_UP_ATT:
            for (int i = 0; i < PA_NUM; i++)
            {
                if(addr_code == (hx_pa_addr[i].addr_code & 0x0F))
                {
                    if( hx_pa_present[i] )
                    {
                        hx_pa_status status;
                        ret = hx_get_up_att(pa_serial_fd, &hx_pa_addr[i], &status); 
                        if( pa_ret_ok != ret)
                        {
                            PALogError("get hx pa up_att failed.");
                            return ret;
                        }
                        lpa_status.pa_upatt = status.upatt;
                    }
                    else
                    {
                        ret = pa_not_found;
                    }
                }
            }  

            *value = lpa_status.pa_upatt;        
            break;
			
         case PA_STATUS_SW10RADIO:
            for (int i = 0; i < PA_NUM; i++)
            {
                if(addr_code == (hx_pa_addr[i].addr_code & 0x0F))
                {
                    if( hx_pa_present[i] )
                    {
                        hx_pa_status status;
                        ret = hx_get_pa_10_swr(pa_serial_fd, &hx_pa_addr[i], &status);
                        if( pa_ret_ok != ret)
                        {
                            PALogError("get hx pa sw10radio failed.");
            	            return ret;
        	            }
                        lpa_status.pa_sw10ratio = status.sw10ratio;
                    }
                    else
                    {
                        ret = pa_not_found;
                    }
                }
            }              

            *value = lpa_status.pa_sw10ratio;
            break;
        
        case PA_STATUS_DN_ATT:
            for (int i = 0; i < PA_NUM; i++)
            {
                if(addr_code == (hx_pa_addr[i].addr_code & 0x0F))
                {
                    if( hx_pa_present[i] )
                    {
                        hx_pa_status status;
                        ret = hx_get_dn_att(pa_serial_fd, &hx_pa_addr[i], &status);
                        if( pa_ret_ok != ret)
                        {
                            PALogError("get hx pa dn_att failed.");
            	            return ret;
                        }
                        lpa_status.pa_dnatt = status.dnatt;
                    }
                    else
                    {
                        ret = pa_not_found;
                    }
                }
            }

            *value = lpa_status.pa_dn_power;
            break;

        case PA_STATUS_DN_10_POWER:
            for (int i = 0; i < PA_NUM; i++)
            {
                if(addr_code == (hx_pa_addr[i].addr_code & 0x0F))
                {
                    if( hx_pa_present[i] )
                    {
                        hx_pa_status status;
                        ret = hx_get_dn_10_power(pa_serial_fd, &hx_pa_addr[i], &status);
                        if( pa_ret_ok != ret)
                        {
            	            PALogError("get hx pa dn_10_power failed.");
            	            return ret;
        	            }
                        lpa_status.pa_dn_10_power= status.dn10power;
                    }
                    else
                    {
                        ret = pa_not_found;
                    }
                }
            }            

            *value = (lpa_status.pa_dn_10_power&0x00FF);
            *(value+1) = ((lpa_status.pa_dn_10_power)>>8);
            break;            

		case PA_STATUS_TEMPTHRES:
            for (int i = 0; i < PA_NUM; i++)
            {
                if(addr_code == (hx_pa_addr[i].addr_code & 0x0F))
                {
                    if( hx_pa_present[i] )
                    {
                        hx_pa_status status;
                        ret = hx_get_pa_temp_threshold(pa_serial_fd, &hx_pa_addr[i], &status); 
                        if( pa_ret_ok != ret)
                        {
                            PALogError("get hx pa temp threshold failed.");
                            return ret;
                        }
                        lpa_status.pa_tempthres = status.tempthreshold;
                    }
                    else
                    {
                        ret = pa_not_found;
                    }
                }
            }  

            *value = lpa_status.pa_tempthres;        
            break;

		 case PA_STATUS_POWERTHRES:
            for (int i = 0; i < PA_NUM; i++)
            {
                if(addr_code == (hx_pa_addr[i].addr_code & 0x0F))
                {
                    if( hx_pa_present[i] )
                    {
                        hx_pa_status status;
                        ret = hx_get_pa_power_threshold(pa_serial_fd, &hx_pa_addr[i], &status);
                        if( pa_ret_ok != ret)
                        {
            	            PALogError("get hx pa power threshold failed.");
            	            return ret;
        	            }
                        lpa_status.pa_powerthres= status.powerthreshold;
                    }
                    else
                    {
                        ret = pa_not_found;
                    }
                }
            }            

            *value = lpa_status.pa_powerthres;
            break;  

        default:
            PALogError("entry %d not supported", entry);
            return pa_not_supported;
        }
    
	return ret;
}
  
pa_ret_t initPaCom(uint8_t *ptr)
{
    PARAM *tlv = (PARAM *)ptr;
    int paState[PA_NUM+1] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    if (tlv->paraLength == sizeof(int) * (PA_NUM+1))
    {
        memcpy(paState, tlv->paraValue, sizeof(int) * (PA_NUM+1));

        for (int i = 0; i < PA_NUM; i++)
        {
            if(paState[i])
            {
                hx_pa_present[i] = true;
            }
        }
		tempwarnThreshold = paState[PA_NUM];
		PALogNotice("tempwarn is %d \n", tempwarnThreshold);
    }

    init_uart_serial(1);
   
    if( set_uart_serial_opt(pa_serial_fd, 9600, 8, 'N' , 1) < 0 )
    {
        PALogError("Set serial option failed");
        return pa_err_uart;
    }

    for (int i = 0; i < PA_NUM; i++)
    {
        if(hx_pa_present[i] == true)
        {
            PALogNotice("pa_number_%d is active and shutdown power", i);
            set_pa_switch((i+1), 1);
        }
        else
        {
            PALogNotice("pa_number_%d is not active", i);
        }
    }

    bool pa_present = false;
    for (int i = 0; i < PA_NUM; i++)
    {
        pa_present |= hx_pa_present[i];
    }

    if (pa_present)
         return pa_ret_ok;
    else
         return pa_not_found; 	
}

void termPaCom()
{
    term_uart_serial();
    pa_number = 0;
}

int32_t get_pa_temp_warnthreshold()
{
	return tempwarnThreshold;
}

